#include "Parser.h"
//#include <QString>
#include <utility>
//#include <QDebug>
//#include <windows.h>
#include "Tree.h"
#include <stdlib.h>
void replace(string &a, const string &substr, const string &format)
{
    regex r(substr);
    a = regex_replace(a, r, format);
}

void htmlOptmize(string &a)
{
    for (auto &i : htmlWords)
    {
        replace(a, i.first, i.second);
    }
    replace(a, "\"", "&quot");
}

map<types, regex> typeStore = {
    //Inline
    {BOLD, regex(R"(\*\*([^*^\n]+)\*\*)")},
    {TITLE, regex("^[\\s]*#+[ ]+(.+)")},
    //{URL, regex(R"(\[(.*)?\][\S]*\((.*)?\))")},
    {ITALIC, regex("(\\*([^*^\n]+)\\*)")},
    {STRONG, regex(R"(\*\*([^*^\n]+)\*\*)")},
    {CODE, regex("`([^`]+)`")},

    //Paragraph
    {LINE, regex("^[\\s]*[-+\\*]{3,}[\\s]*$")},
    {ULIST, regex("^[\\s]*[-\\*\\+] ([\\s\\S]*)")},
    {OLIST, regex("^[\\s]*[\\d]+\\. ([\\s\\S]*)")},
    {CODEBLOCK, regex("```")},
    {QUOTEBLOCK, regex(">([\\s\\S]*)")},
    {PHOTO, regex(R"(!\[(.*?)\]\((.*?)\))")},
    {LINK, regex(R"(\[(.*?)\]\((.*?)\))")}}; //*/

vector<char> protect = {'*', '`', '.', '+', '-', '\\', '\'', '\"'};

bool validLabel(string s)
{ //康康是不是html标签
    if (s.length() <= 1)
    {
        return false;
    }
    return s[0] == '<' && s[s.length() - 1] == '>';
}

vector<string> inlineCutter(string fish)
{ //把html标签和正常内容切开
    vector<string> cut;
    if (fish.empty())
    {
        return cut;
    }
    string temp;
    temp = fish[0];
    fish.erase(0, 1);
    bool inLabel = temp == "<"; //标签还是字
    bool inCode = temp == "`";
    for (auto i : fish)
    {
        if (i == '`')
        {
            if (inCode)
            {
                inCode = false;
                temp += i;
                continue;
            }
            else
            {
                inCode = true;
                temp += i;
                continue;
            }
        }
        if (inLabel && !inCode)
        {
            temp += i;
            if (i == '>')
            {
                cut.push_back(temp);
                temp = "";
                inLabel = false;
            }
        }
        else if (!inCode)
        {
            if (i == '<')
            {
                cut.push_back(temp);
                temp = "";
                temp += i;
                inLabel = true;
            }
            else
            {
                temp += i;
            }
        }
        else
        {
            temp += i;
        }
    }
    cut.push_back(temp);
    return cut;
}

bool validTable(string s)
{
    int middle = 0;
    for (int i = 0; s[i] != '\0'; i++)
    {
        if (s[i] == ' ' || s[i] == '\t')
        {
            s.erase(i, 1);
            i--;
        }
    }
    bool flag1 = (s[0] == '|') && (s[s.length() - 1] == '|');
    //cout << s << endl;
    for (auto i : s)
    {
        if (i == '|')
        {
            middle++;
        }
    }
    return flag1 && middle >= 3;
}

vector<string> align(string s)
{
    s.erase(0, 1);
    vector<string> ali;
    stringstream st(s.c_str());
    string temp;
    while (getline(st, temp, '|'))
    {
        ali.emplace_back(temp);
    }
    return ali;
}

vector<string> ali(string s)
{
    vector<string> raw = align(s);
    bool f1 = false;
    bool f2 = false;
    for (auto &i : raw)
    {
        if (i[0] == ':')
        {
            f1 = true;
        }
        if (i[i.length() - 1] == ':')
        {
            f2 = true;
        }
        if (f1 && f2)
        {
            i = "middle";
        }
        else if (f1)
        {
            i = "left";
        }
        else if (f2)
        {
            i = "right";
        }
        else
        {
            i = "middle";
        }
    }
    return raw;
}

void tabToBlank(string &fish)
{
    for (int i = 0; fish[i] != '\0'; i++)
    {
        if (isspace(fish[i]))
        {
            if (fish[i] == '\t')
            {
                fish.erase(i, 1);
                fish.insert(i, "    ");
            }
        }
        else
        {
            break;
        }
    }
}

vector<string> listCut(string lst)
{
    stringstream cutter;
    cutter << lst;
    string a;
    getline(cutter, a);
    a += "\n";
    vector<string> cut;
    string temp;
    while (getline(cutter, temp))
    {
        tabToBlank(temp);
        if (regex_match(temp, typeStore[ULIST]) || regex_match(temp, typeStore[OLIST]))
        {
            cut.emplace_back(a);
            a = "";
        }
        a += temp + '\n';
    }
    cut.emplace_back(a);
    return cut;
}

int Counter(string &s, char c)
{
    int i = 0;
    while (s[i] == c)
    {
        i++;
    }
    s.erase(0, i);
    return i;
}

Lexer::Lexer(string raw)
{
    tokenize(raw);
}

vector<string> Lexer::outPut()
{
    return tokens;
}

void Lexer::print()
{
    int counter = 0;
    for (const auto &i : tokens)
    {
        if (i.empty())
        {
            cout << "\n"
                 << counter << ". empty" << endl;
        }
        else
        {
            cout << counter << ". ";
            cout << i << endl;
        }
        counter++;
    }
}

void Lexer::tokenize(string raw)
{
    string temp;
    stringstream file = stringstream(raw);
    while (getline(file, temp))
    {
        //cout << temp << endl;
        if (regex_match(temp, regex(typeStore[ULIST])) || regex_match(temp, regex(typeStore[OLIST])))
        {
            string a;
            a += temp + '\n';
            bool flag = false;
            bool isCode = false;
            while (getline(file, temp))
            {
                if (regex_match(temp, regex("[\\s]*```(.*)")))
                {
                    isCode = !isCode;
                }
                //cout << temp << endl;
                if (temp.empty() && !isCode)
                {
                    flag = true;
                    tokens.emplace_back(a);
                    tokens.emplace_back(temp);
                    break;
                }
                else
                {
                    a += temp + '\n';
                }
            }
            if (!flag)
            {
                tokens.emplace_back(a);
            }
        }
        else if (regex_match(temp, typeStore[QUOTEBLOCK]))
        {
            string a;
            a += temp + '\n';
            bool flag = false;
            while (getline(file, temp))
            {
                if (regex_match(temp, typeStore[QUOTEBLOCK]))
                {
                    a += temp + '\n';
                }
                else
                {
                    flag = true;
                    tokens.emplace_back(a);
                    tokens.emplace_back(temp);
                    break;
                }
            }
            if (!flag)
            {
                tokens.emplace_back(a);
                tokens.emplace_back(temp);
            }
            //cout << regex_match(a,typeStore[QUOTEBLOCK]) <<endl;
        }
        else
        {
            tokens.emplace_back(temp);
        }
    }
}

void Parser::tinyParse(string s, string *product)
{
    vector<string> cut = inlineCutter(s);
    for (auto i : cut)
    {
        if (validLabel(i))
        {
            *product += i;
        }
        else
        {
            htmlOptmize(i);
            //STRONG
            i = regex_replace(i, regex("\\*\\*(.+?)\\*\\*"), "<strong>$1</strong>");
            //ITALIC
            i = regex_replace(i, regex("\\*(.+?)\\*"), "<em>$1</em>");
            i = regex_replace(i, regex("~~(.+?)~~"), "<del>$1</del>");
            i = regex_replace(i, typeStore[PHOTO], "<img src=\"$2\" alt=\"$1\" />");
            i = regex_replace(i, typeStore[LINK], R"(<a href="$2" target="_blank">$1</a>)");
            if (code)
            {
                i = regex_replace(i, typeStore[CODE], "<code>$1</code>");
                string temp = "";
                vector<string> codeCut = inlineCutter(i);
                bool codebegin = false;
                bool img = false;
                string linktemp;
                for (auto j : codeCut)
                {
                    if (codebegin)
                    {
                        if (j == "</code>")
                        {
                            codebegin = false;
                        }
                        else
                        {
                            if (img)
                            {
                                j = "[" + j + "](" + linktemp + ")";
                                img = false;
                                linktemp = "";
                            }
                            else if (j == "<strong>" || j == "</strong>")
                            {
                                j = "**";
                            }
                            else if (j == "<em>" || j == "</em>")
                            {
                                j = "*";
                            }
                            else if (j == "<del>" || j == "</del>")
                            {
                                j = "~~";
                            }
                            else if (regex_match(j, regex("<a href=\".*\" target=\"_blank\">")))
                            {
                                img = true;
                                linktemp = regex_replace(j, regex("<a href=\"(.*)\" target=\"_blank\">"), "$1");
                                j = "";
                            }
                            else if (j == "</a>")
                            {
                                j = "";
                            }
                            else if (regex_match(j, regex("<img src=\".*?\" alt=\".*?\" />")))
                            {
                                j = regex_replace(j, regex("<img src=\"(.*)\" alt=\"(.*)\" />"), "![$2]($1)");
                            }
                        }
                    }
                    else
                    {
                        if (j == "<code>")
                        {
                            codebegin = true;
                        }
                    }
                    //cout << Utf8ToGbk(j.c_str());
                    temp += j;
                }
                i = temp;
                //死者苏生是吧😅
                /*i = regex_replace(i, typeStore[CODE], "<code>$1</code>");
                                i = regex_replace(i, regex("(<code>)(.*?)<strong>(.*?)</strong>(.*?)(</code>)"), "$1$2**$3**$4$5");
                                i = regex_replace(i, regex("(<code>)(.*)(<em>)(.*)(</em>)(.*)(</code>)"), "$1$2*$4*$6$7");
                                i = regex_replace(i, regex("(<code>)(.*)(<del>)(.*)(</del>)(.*)(</code>)"), "$1$2~~$4~~$6$7");
                                i = regex_replace(i, regex("(<code>)(.*)<a href=\"(.*)\" target=\"_blank\">(.*)</a>(.*)(</code>)"), "$1$2[$4]($3)$5$6");
                                i = regex_replace(i, regex("(<code>)(.*)<img src=\"(.*)\" alt=\"(.*)\" />(.*)(</code>)"), "$1$2![$4]($3)$5$6");*/
            }
            i = regex_replace(i, regex("([ ]{2,})$"), "<br />");
            *product += i;
        }
    }
}

int Parser::parseOneLine(int now, string *product, vector<string> aligins)
{

    if (now == tokens.size())
    {
        if (env[PARA])
        {
            *product += "</p>";
        }
        return now + 1;
    }
    string nowLine = tokens[now];
    // begin to write body.
    if (nowLine.empty())
    {
        if (env[PARA])
        {
            env[PARA] = false;
            *product += "</p>\n";
        }
        now++;
        return now;
    }
    else
    {
        int i;
        if ((regex_match(nowLine, regex(typeStore[ULIST])) || regex_match(nowLine, regex(typeStore[OLIST]))) && !env[CODEBLOCK])
        {
            Tree<string> lists = tree_generator(nowLine);
            parseList(&lists, product);
            return now + 1;
        } //列表，到时候再写。(
        else if (regex_match(nowLine, typeStore[QUOTEBLOCK]) && !env[CODEBLOCK])
        {
            *product += "<blockquote>";
            stringstream s(nowLine.c_str());
            string temp;
            string prod;
            while (getline(s, temp))
            {
                temp = regex_replace(temp, typeStore[QUOTEBLOCK], "$1");
                Counter(temp, ' ');
                prod += temp + '\n';
            }
            smallParse(prod, product);
            *product += "</blockquote>";
            return now + 1;
        }
        else if (!env[CODEBLOCK] && (regex_match(nowLine, typeStore[TITLE])))
        {
            if (env[PARA])
            {
                *product += "</p>\n";
            }
            env[PARA] = false;
            nowLine = regex_replace(nowLine, regex("^[ ]*(.*)"), "$1");
            i = Counter(nowLine, '#');
            i = i > 6 ? 6 : i;
            *product += "<h";
            *product += '0' + i;
            *product += ">\n";
            tokens[now] = nowLine;
            env[TITLE] = true;
            now = parseOneLine(now, product);
            env[TITLE] = false;
            *product += "</h";
            *product += '0' + i;
            *product += ">\n";
            if (env[QUOTEBLOCK])
            {
                return parseOneLine(now, product);
            }
            if (env[PARA])
            {
                *product += "</p>\n";
                env[PARA] = false;
            }
            return now;
        }
        else if (regex_match(nowLine, typeStore[LINE]) && !env[CODEBLOCK])
        {
            if (env[PARA])
            {
                *product += "</p>\n";
            }
            env[PARA] = false;
            *product += "<hr />\n";
            if (env[QUOTEBLOCK])
            {
                return parseOneLine(now + 1, product);
            }
            return now + 1;
        }
        else if (regex_match(nowLine, regex("[\\s]*```(.*)")))
        {
            string codeClass = regex_replace(nowLine, regex("[\\s]*```(.*)"), "$1");
            if (env[PARA])
            {
                *product += "</p>\n";
            }
            env[PARA] = false;
            if (!env[CODEBLOCK])
            {
                env[CODEBLOCK] = true;
                htmlOptmize(codeClass);
                *product += "<pre><code class=\"language-" + codeClass + "\">";
            }
            else
            {
                env[CODEBLOCK] = false;
                *product += "</code></pre>\n";
            }
            if (env[QUOTEBLOCK])
            {
                return parseOneLine(now + 1, product);
            }
            return now + 1;
        }
        else if (validTable(nowLine) && !env[CODEBLOCK])
        { /*now <= tokens.size() - 3 && */
            if (!env[TABLE])
            {
                if (now <= tokens.size() - 3 && validTable(tokens[now + 1]) && validTable(tokens[now + 2]))
                {
                    env[TABLE] = true;
                    if (env[PARA])
                    {
                        *product += "</p>\n";
                    }
                    env[PARA] = false;
                    *product += "<table border=\"1\">\n";
                    vector<string> a = align(nowLine);
                    vector<string> lign = ali(tokens[now + 1]);
                    *product += "<tr>\n";
                    for (int i = 0; i < min(lign.size(), a.size()); i++)
                    {
                        *product += "<th align=\"" + lign[i] + "\">";
                        tinyParse(a[i], product);
                        *product += "</th>\n";
                    }
                    *product += "</tr>\n";
                    now = parseOneLine(now + 2, product, lign);
                    *product += "</table>";
                    env[TABLE] = false;
                    if (env[QUOTEBLOCK])
                    {
                        return parseOneLine(now, product);
                    }
                    return now;
                }
                else
                {
                    tinyParse(nowLine, product);
                    return now + 1;
                }
            }
            else
            {
                vector<string> a = align(nowLine);
                *product += "<tr>\n";
                for (int i = 0; i < min(aligins.size(), a.size()); i++)
                {
                    *product += "<td align=\"" + aligins[i] + "\">";
                    tinyParse(a[i], product);
                    *product += "</td>\n";
                }
                *product += "</tr>\n";
                bool isNext = now < tokens.size() - 1 && validTable(tokens[now + 1]);
                if (isNext)
                {
                    return parseOneLine(now + 1, product, aligins);
                }
                else
                {
                    if (env[QUOTEBLOCK])
                    {
                        return parseOneLine(now + 1, product);
                    }
                    return now + 1;
                }
            }
        }
        else
        {
            if (!env[PARA] && !env[TITLE] && !env[CODEBLOCK])
            {
                *product += "<p>";
                env[PARA] = true;
            }
            if (!env[CODEBLOCK])
            {
                vector<string> cut = inlineCutter(nowLine);
                for (auto i : cut)
                {
                    if (validLabel(i))
                    {
                        *product += i;
                    }
                    else
                    {
                        htmlOptmize(i);
                        //STRONG
                        i = regex_replace(i, regex("\\*\\*(.+?)\\*\\*"), "<strong>$1</strong>");
                        //ITALIC
                        i = regex_replace(i, regex("\\*(.+?)\\*"), "<em>$1</em>");
                        i = regex_replace(i, regex("~~(.+?)~~"), "<del>$1</del>");
                        i = regex_replace(i, typeStore[PHOTO], "<img src=\"$2\" alt=\"$1\" />");
                        i = regex_replace(i, typeStore[LINK], R"(<a href="$2" target="_blank">$1</a>)");
                        if (code)
                        {
                            i = regex_replace(i, typeStore[CODE], "<code>$1</code>");
                            string temp = "";
                            vector<string> codeCut = inlineCutter(i);
                            bool codebegin = false;
                            bool img = false;
                            string linktemp;
                            for (auto j : codeCut)
                            {
                                if (codebegin)
                                {
                                    if (j == "</code>")
                                    {
                                        codebegin = false;
                                    }
                                    else
                                    {
                                        if (img)
                                        {
                                            j = "[" + j + "](" + linktemp + ")";
                                            img = false;
                                            linktemp = "";
                                        }
                                        else if (j == "<strong>" || j == "</strong>")
                                        {
                                            j = "**";
                                        }
                                        else if (j == "<em>" || j == "</em>")
                                        {
                                            j = "*";
                                        }
                                        else if (j == "<del>" || j == "</del>")
                                        {
                                            j = "~~";
                                        }
                                        else if (regex_match(j, regex("<a href=\".*\" target=\"_blank\">")))
                                        {
                                            img = true;
                                            linktemp = regex_replace(j, regex("<a href=\"(.*)\" target=\"_blank\">"), "$1");
                                            j = "";
                                        }
                                        else if (j == "</a>")
                                        {
                                            j = "";
                                        }
                                        else if (regex_match(j, regex("<img src=\".*?\" alt=\".*?\" />")))
                                        {
                                            j = regex_replace(j, regex("<img src=\"(.*)\" alt=\"(.*)\" />"), "![$2]($1)");
                                        }
                                    }
                                }
                                else
                                {
                                    if (j == "<code>")
                                    {
                                        codebegin = true;
                                    }
                                }
                                //cout << Utf8ToGbk(j.c_str());
                                temp += j;
                            }
                            i = temp;
                            //死者苏生是吧😅
                            /*i = regex_replace(i, typeStore[CODE], "<code>$1</code>");
                                i = regex_replace(i, regex("(<code>)(.*?)<strong>(.*?)</strong>(.*?)(</code>)"), "$1$2**$3**$4$5");
                                i = regex_replace(i, regex("(<code>)(.*)(<em>)(.*)(</em>)(.*)(</code>)"), "$1$2*$4*$6$7");
                                i = regex_replace(i, regex("(<code>)(.*)(<del>)(.*)(</del>)(.*)(</code>)"), "$1$2~~$4~~$6$7");
                                i = regex_replace(i, regex("(<code>)(.*)<a href=\"(.*)\" target=\"_blank\">(.*)</a>(.*)(</code>)"), "$1$2[$4]($3)$5$6");
                                i = regex_replace(i, regex("(<code>)(.*)<img src=\"(.*)\" alt=\"(.*)\" />(.*)(</code>)"), "$1$2![$4]($3)$5$6");*/
                        }
                        i = regex_replace(i, regex("([ ]{2,})$"), "<br />");
                        *product += i;
                    }
                }
            }
            else
            {
                htmlOptmize(nowLine);
                *product += nowLine + '\n';
            }
            now++;
            if (env[QUOTEBLOCK])
            {
                return parseOneLine(now, product);
            }
            return now;
        }
    }
}

void Parser::parseCheat(string name, string target, string css) //2333333333333333333
{
    system(("pandoc " + name + " -o " + target).c_str());
}

Parser::Parser(vector<string> tokens, bool code)
{
    this->tokens = std::move(tokens);
    for (int i = 0; i < 13; i++)
    {
        env[i] = false;
    }
    this->code = code;
}

void Parser::smallParse(string temp, string *product)
{
    Lexer l(temp + "\n\n");
    Parser p(l.outPut(), code);
    int size = l.outPut().size();
    for (int i = 0; i <= size; i = p.parseOneLine(i, product))
        ;
}

void Parser::parseList(Tree<string> *t, string *product)
{
    if (t->root())
    {
        if (t->leaf())
        {
            return;
        }
        else
        {
            bool ul = regex_match(t->branches[0]->item(), typeStore[ULIST]);
            bool ol = regex_match(t->branches[0]->item(), typeStore[OLIST]);
            if (ul)
            {
                *product += "<ul>";
                for (auto i : t->branches)
                {
                    parseList(i, product);
                }
                *product += "</ul>\n";
            }
            else
            {
                *product += "<ol>";
                for (auto i : t->branches)
                {
                    parseList(i, product);
                }
                *product += "</ol>\n";
            }
        }
    }
    else if (t->leaf())
    {
        bool ul = regex_match(t->item(), typeStore[ULIST]);
        bool ol = regex_match(t->item(), typeStore[OLIST]);
        string s = t->item();
        if (ul)
        {
            s = regex_replace(s, typeStore[ULIST], "$1");
        }
        else
        {
            s = regex_replace(s, typeStore[OLIST], "$1");
        }
        *product += "<li>";
        smallParse(s, product);
        *product += "</li>\n";
    }
    else
    {
        bool ul = regex_match(t->item(), typeStore[ULIST]);
        bool ol = regex_match(t->item(), typeStore[OLIST]);
        string s = t->item();
        if (ul)
        {
            s = regex_replace(s, typeStore[ULIST], "$1");
        }
        else
        {
            s = regex_replace(s, typeStore[OLIST], "$1");
        }
        ul = regex_match(t->branches[0]->item(), typeStore[ULIST]);
        ol = regex_match(t->branches[0]->item(), typeStore[OLIST]);
        if (ul)
        {
            *product += "<li>\n";
            smallParse(s, product);
            *product += "<ul>";
            for (auto i : t->branches)
            {
                parseList(i, product);
            }
            *product += "</ul>\n</li>\n";
        }
        else
        {
            *product += "<li>\n";
            smallParse(s, product);
            *product += "<ol>";
            for (auto i : t->branches)
            {
                parseList(i, product);
            }
            *product += "</ol>\n</li>\n";
        }
    }
}
